package org.tinygroup.xmlsignature.impl;

import java.io.OutputStream;
import java.security.KeyPair;
import java.util.Collections;
import java.util.List;

import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;

import org.tinygroup.xmlsignature.config.XmlSignatureConfig;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 处理Enveloped格式的XML数字签名(签名数据包含Signature元素)
 * @author yancheng11334
 *
 */
public class EnvelopedXmlSignatureProcessor extends DsigXmlSignatureProcessor{

	protected List<Reference> createReference(Document doc,
			XmlSignatureConfig config) throws Exception {
		Transform envelopedTransform = xmlSignatureFactory.newTransform(Transform.ENVELOPED,
				(TransformParameterSpec) null);
		DigestMethod sha1DigMethod =xmlSignatureFactory.newDigestMethod(DigestMethod.SHA1, null);
		Reference ref = xmlSignatureFactory.newReference("", sha1DigMethod,Collections.singletonList(envelopedTransform),null,null);
		return Collections.singletonList(ref);
	}

	protected void createXmlSignature(XmlSignatureConfig config, Document doc,
			OutputStream output) throws XMLSignatureException {
		try{
			CanonicalizationMethod cm = createCanonicalizationMethod(config);
			SignatureMethod   sm = createSignatureMethod(config);
			List<Reference> references = createReference(doc,config);
			SignedInfo si = createSignedInfo(cm, sm, references);
			KeyInfo  ki = createKeyInfo(config);
			XMLSignature signature = createXMLSignature(si, ki);
			
			KeyPair keyPair = getXmlSignatureManager().getKeyPair(config.getUserId());
			DOMSignContext signContext = new DOMSignContext(keyPair.getPrivate(),doc.getDocumentElement());
			signature.sign(signContext);
			
			transform(doc, output);
		}catch(Exception e){
			throw new XMLSignatureException("生成Enveloped格式的XML数字签名失败",e);
		}
	}

	protected boolean validateXmlSignature(XmlSignatureConfig config,
			Document doc) throws XMLSignatureException {
		try{
			// 查找签名元素
			NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,
					"Signature");
			if (nl.getLength() == 0) {
				throw new Exception("没有找到<Signature>元素");
			}
			Node signatureNode = nl.item(0);
			XMLSignature signature = xmlSignatureFactory.unmarshalXMLSignature(new DOMStructure(signatureNode));
			
			KeyPair keyPair = getXmlSignatureManager().getKeyPair(config.getUserId());
			DOMValidateContext valCtx = new DOMValidateContext(keyPair.getPublic(),
					signatureNode);
			return signature.validate(valCtx);
		}catch(Exception e){
			throw new XMLSignatureException("验证Enveloped格式的XML数字签名失败",e);
		}
	}

}
